/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
  <file> uls_lf_sprintf.c </file>
  <brief>
    An upgraded version of varargs routines(sprintf,fprintf,printf, ..).
    This file is part of ULS, Unified Lexical Scheme.
  </brief>
  <author>
    Stanley J. Hong <babel96@gmail.com>, July 2011.
  </author>
*/

#include "common.h"
#include "uls_lf_sprintf.h"
#include "uls_lf_percent_f.h"
#include "ieee754.h"
#include "uls_log.h"

#include <wchar.h>

uls_lf_t *dfl_uls_lf;
static DECLARE_ULS_MUTEX(uls_lf_genport_mtx);
static uls_lf_map_t dfl_convspec_map;

static void
__uls_lf_sysputs(const char* msg)
{
	/* NOTHING */
}

void uls_lf_puts_csz(void* x_dat, const uls_lf_char_t* wrdptr, int wrdlen)
{
	csz_append((csz_str_t *) x_dat, (uls_char_t *) wrdptr, wrdlen);
}

void uls_lf_puts_str(void* x_dat, const uls_lf_char_t* wrdptr, int wrdlen)
{
	buf4legacy_t *lb = (buf4legacy_t *) x_dat;

	if (wrdptr == NULL) {
		if (lb->buflen <= 0) {
			__uls_lf_sysputs("sprintf: buffer overflow");
			return;
		}
		*lb->bufptr = '\0';
	}

	if (lb->buflen <= wrdlen) {
		__uls_lf_sysputs("sprintf: buffer overflow");
		return;
	}

	if (wrdlen > 0) {
		memcpy(lb->bufptr, wrdptr, wrdlen*sizeof(uls_lf_char_t));
		lb->bufptr += wrdlen;
		lb->buflen -= wrdlen;
	}
}

void uls_lf_puts_file(void* x_dat, const uls_lf_char_t* wrdptr, int wrdlen)
{
	FILE *fout = (FILE *) x_dat;

	if (wrdptr == NULL) {
		fflush(fout);
		return;
	}

	if (fwrite(wrdptr, sizeof(uls_lf_char_t), wrdlen, fout) < (size_t) wrdlen) {
//		err_log_puts("uls_lf_puts_file: fwrite error");
	}
}

void uls_lf_puts_null(void* x_dat, const uls_lf_char_t* wrdptr, int wrdlen)
{
}

static int
fmtproc_s(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t  *wrdptr;
	int len;
	size_t wlen;

	wrdptr = (uls_lf_char_t* ) va_arg(ctx->args, uls_lf_char_t *);
	if (wrdptr==NULL) wrdptr = (uls_lf_char_t *) "<NULL>";

	len = uls_strlen(wrdptr);
	if ((wlen = mbstowcs(NULL, (char *) wrdptr, 0)) == (size_t) -1) {
		return -1;
	}

	return uls_lf_fill_mbstr(ctx->x_dat, puts_proc, &ctx->perfmt, wrdptr, len, wlen);
}

static int
fmtproc_c(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	int ch;
	uls_lf_char_t  buf[2];

	ch = (int) va_arg(ctx->args, int);

	buf[0] = (uls_lf_char_t) ch;
	puts_proc(ctx->x_dat, buf, 1);
	return 1;
}

static int
fmtproc_d(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t buf[ULS_MAXSZ_NUMSTR+1];
	uls_lf_char_t *wrdptr;
	int idx, minus;
	int num_d, len;

	num_d = (int) va_arg(ctx->args, int);
	if (num_d < 0) {
		num_d = -num_d;
		minus = 1;
	} else {
		minus = 0;
	}

	idx = uls_lf_number_u(buf, num_d, ULS_LF_SHIFT_BASE_DEC);
	if (minus) ctx->perfmt.flags |= ULS_LF_MINUS_PREFIX;

	wrdptr = buf + idx;
	len = ULS_MAXSZ_NUMSTR - idx;

	return uls_lf_fill_chars(ctx->x_dat, puts_proc, &ctx->perfmt, wrdptr, len);
}

static int
fmtproc_u(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t buf[ULS_MAXSZ_NUMSTR+1];
	uls_lf_char_t *wrdptr;
	unsigned int num_u;
	int idx, len;

	num_u = (unsigned int) va_arg(ctx->args, unsigned int);
	idx = uls_lf_number_u(buf, num_u, ULS_LF_SHIFT_BASE_DEC); 
	wrdptr = buf + idx;
	len = ULS_MAXSZ_NUMSTR - idx;

	return uls_lf_fill_chars(ctx->x_dat, puts_proc, &ctx->perfmt, wrdptr, len);
}

static int
fmtproc_Ld(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t buf[ULS_MAXSZ_NUMSTR+1];
	uls_lf_char_t *wrdptr;
	long long num_Ld;
	int idx, minus, len;

	num_Ld = (long long) va_arg(ctx->args, long long);
	if (num_Ld < 0) {
		num_Ld = -num_Ld;
		minus = 1;
	} else {
		minus = 0;
	}

	idx = uls_lf_number_Lu(buf, num_Ld, ULS_LF_SHIFT_BASE_DEC);
	if (minus) ctx->perfmt.flags |= ULS_LF_MINUS_PREFIX;

	wrdptr = buf + idx;
	len = ULS_MAXSZ_NUMSTR - idx;

	return uls_lf_fill_chars(ctx->x_dat, puts_proc, &ctx->perfmt, wrdptr, len);
}

static int
fmtproc_Lu(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t buf[ULS_MAXSZ_NUMSTR+1];
	uls_lf_char_t *wrdptr;
	unsigned long long num_Lu;
	int idx, len;

	num_Lu = (unsigned long long) va_arg(ctx->args, unsigned long long);
	idx = uls_lf_number_Lu(buf, num_Lu, ULS_LF_SHIFT_BASE_DEC);
	wrdptr = buf + idx;
	len = ULS_MAXSZ_NUMSTR - idx;

	return uls_lf_fill_chars(ctx->x_dat, puts_proc, &ctx->perfmt, wrdptr, len);
}

static int
fmtproc_LX(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t buf[ULS_MAXSZ_NUMSTR+1];
	uls_lf_char_t *wrdptr;
	unsigned long long num_Lu;
	int idx, len;

	num_Lu = (unsigned long long) va_arg(ctx->args, unsigned long long);
	idx = uls_lf_number_Lu(buf, num_Lu, ULS_LF_SHIFT_BASE_HEX);
	wrdptr = buf + idx;
	len = ULS_MAXSZ_NUMSTR - idx;

	if (ctx->perfmt.flags & ULS_LF_SPECIAL_CHAR) {
		ctx->perfmt.flags |= ULS_LF_HEX_PREFIX;
	}

	return uls_lf_fill_chars(ctx->x_dat, puts_proc, &ctx->perfmt, wrdptr, len);
}

static int
fmtproc_Lx(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t buf[ULS_MAXSZ_NUMSTR+1];
	uls_lf_char_t *wrdptr;
	unsigned long long num_Lu;
	int idx, len, i;

	num_Lu = (unsigned long long) va_arg(ctx->args, unsigned long long);
	idx = uls_lf_number_Lu(buf, num_Lu, ULS_LF_SHIFT_BASE_HEX);
	wrdptr = buf + idx;
	len = ULS_MAXSZ_NUMSTR - idx;

	for (i=0; i<len; i++)
		wrdptr[i] = tolower(wrdptr[i]);

	if (ctx->perfmt.flags & ULS_LF_SPECIAL_CHAR) {
		ctx->perfmt.flags |= ULS_LF_HEX_PREFIX;
	}

	return uls_lf_fill_chars(ctx->x_dat, puts_proc, &ctx->perfmt, wrdptr, len);
}

static int
fmtproc_X(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t buf[ULS_MAXSZ_NUMSTR+1];
	uls_lf_char_t *wrdptr;
	unsigned int num_u;
	int idx, len;

	num_u = (unsigned int) va_arg(ctx->args, unsigned int);

	idx = uls_lf_number_u(buf, num_u, ULS_LF_SHIFT_BASE_HEX); 
	wrdptr = buf + idx;
	len = ULS_MAXSZ_NUMSTR - idx;

	if (ctx->perfmt.flags & ULS_LF_SPECIAL_CHAR) {
		ctx->perfmt.flags |= ULS_LF_HEX_PREFIX;
	}

	return uls_lf_fill_chars(ctx->x_dat, puts_proc, &ctx->perfmt, wrdptr, len);
}

static int
fmtproc_x(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t buf[ULS_MAXSZ_NUMSTR+1];
	uls_lf_char_t *wrdptr;
	unsigned int num_u;
	int idx, len, i;

	num_u = (unsigned int) va_arg(ctx->args, unsigned int);

	idx = uls_lf_number_u(buf, num_u, ULS_LF_SHIFT_BASE_HEX);
	wrdptr = buf + idx;
	len = ULS_MAXSZ_NUMSTR - idx;

	for (i=0; i<len; i++)
		wrdptr[i] = tolower(wrdptr[i]);

	if (ctx->perfmt.flags & ULS_LF_SPECIAL_CHAR) {
		ctx->perfmt.flags |= ULS_LF_HEX_PREFIX;
	}

	return uls_lf_fill_chars(ctx->x_dat, puts_proc, &ctx->perfmt, wrdptr, len);
}

static int
fmtproc_o(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t buf[ULS_MAXSZ_NUMSTR+1];
	uls_lf_char_t *wrdptr;
	unsigned int num_u;
	int idx, len;

	num_u = (unsigned int) va_arg(ctx->args, unsigned int);

	idx = uls_lf_number_u(buf, num_u, ULS_LF_SHIFT_BASE_OCT);
	wrdptr = buf + idx;
	len = ULS_MAXSZ_NUMSTR - idx;

	return uls_lf_fill_chars(ctx->x_dat, puts_proc, &ctx->perfmt, wrdptr, len);
}

static int
fmtproc_p(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t buf[ULS_MAXSZ_NUMSTR+1];
	uls_lf_char_t *wrdptr;
	unsigned long num_lu;
	int idx, len;

	num_lu = (unsigned long) va_arg(ctx->args, unsigned long);

	idx = uls_lf_number_lu(buf, num_lu, ULS_LF_SHIFT_BASE_HEX); 
	wrdptr = buf + idx;
	len = ULS_MAXSZ_NUMSTR - idx;

	ctx->perfmt.flags |= ULS_LF_ZEROPAD;
	ctx->perfmt.flags &= ~ULS_LF_LEFT_JUSTIFIED;

	ctx->perfmt.width = 2 * sizeof(void *);
	if (ctx->perfmt.flags & ULS_LF_SPECIAL_CHAR) {
		ctx->perfmt.flags |= ULS_LF_HEX_PREFIX;
		ctx->perfmt.width += 2; // +2 for '0x'
	}

	return uls_lf_fill_chars(ctx->x_dat, puts_proc, &ctx->perfmt, wrdptr, len);
}

static int
fmtproc_ld(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t buf[ULS_MAXSZ_NUMSTR+1];
	uls_lf_char_t *wrdptr;
	long num_ld;
	int idx, minus, len;

	num_ld = (long) va_arg(ctx->args, long);
	if (num_ld < 0) {
		num_ld = -num_ld;
		minus = 1;
	} else {
		minus = 0;
	}

	idx = uls_lf_number_lu(buf, num_ld, ULS_LF_SHIFT_BASE_DEC);
	if (minus) ctx->perfmt.flags |= ULS_LF_MINUS_PREFIX;

	wrdptr = buf + idx; 
	len = ULS_MAXSZ_NUMSTR - idx;

	return uls_lf_fill_chars(ctx->x_dat, puts_proc, &ctx->perfmt, wrdptr, len);
}

static int
fmtproc_lu(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t buf[ULS_MAXSZ_NUMSTR+1];
	uls_lf_char_t *wrdptr;
	unsigned long num_lu;
	int idx, len;

	num_lu = (unsigned long) va_arg(ctx->args, unsigned long);
	idx = uls_lf_number_lu(buf, num_lu, ULS_LF_SHIFT_BASE_DEC);
	wrdptr = buf + idx; 
	len = ULS_MAXSZ_NUMSTR - idx;

	return uls_lf_fill_chars(ctx->x_dat, puts_proc, &ctx->perfmt, wrdptr, len);
}

static int
fmtproc_lX(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t buf[ULS_MAXSZ_NUMSTR+1];
	uls_lf_char_t *wrdptr;
	unsigned long num_lu;
	int idx, len;

	num_lu = (unsigned long) va_arg(ctx->args, unsigned long);
	idx = uls_lf_number_lu(buf, num_lu, ULS_LF_SHIFT_BASE_HEX);
	wrdptr = buf + idx; 
	len = ULS_MAXSZ_NUMSTR - idx;

	if (ctx->perfmt.flags & ULS_LF_SPECIAL_CHAR) {
		ctx->perfmt.flags |= ULS_LF_HEX_PREFIX;
	}

	return uls_lf_fill_chars(ctx->x_dat, puts_proc, &ctx->perfmt, wrdptr, len);
}

static int
fmtproc_lx(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t buf[ULS_MAXSZ_NUMSTR+1];
	uls_lf_char_t *wrdptr;
	unsigned long num_lu;
	int idx, len, i;

	num_lu = (unsigned long) va_arg(ctx->args, unsigned long);
	idx = uls_lf_number_lu(buf, num_lu, ULS_LF_SHIFT_BASE_HEX);
	wrdptr = buf + idx; 
	len = ULS_MAXSZ_NUMSTR - idx;

	for (i=0; i<len; i++)
		wrdptr[i] = tolower(wrdptr[i]);

	if (ctx->perfmt.flags & ULS_LF_SPECIAL_CHAR) {
		ctx->perfmt.flags |= ULS_LF_HEX_PREFIX;
	}

	return uls_lf_fill_chars(ctx->x_dat, puts_proc, &ctx->perfmt, wrdptr, len);
}

static int
__fmtproc_f_e_g(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc, double num_f)
{
	uls_lf_convflag_t* perfmt = &ctx->perfmt;
	uls_lf_char_t  *wrdptr;
	int    minus, n_expo, len;
	int    numbuf_len1, numbuf_len2;
	char   *numstr;

	csz_str_t  *numbuf1 = ctx->numbuf1;
	csz_str_t  *numbuf2 = ctx->numbuf2;

	numbuf_len1 = csz_length(numbuf1);
	numbuf_len2 = csz_length(numbuf2);

	if (num_f < 0.) {
		minus = 1;
		num_f = -num_f;
	} else {
		minus = 0;
	}

	n_expo = uls_lf_double2digits(num_f, perfmt->precision, numbuf1);
	numstr = (char *) csz_text(numbuf1) + numbuf_len1;

	if ((perfmt->flags & (ULS_LF_PERCENT_E | ULS_LF_PERCENT_G))==0) {
		uls_lf_digits_to_percent_f(numstr, minus, n_expo, perfmt->precision, numbuf2);
	} else if (perfmt->flags & ULS_LF_PERCENT_E) {
		uls_lf_digits_to_percent_e(numstr, minus, n_expo, perfmt->precision, numbuf2);
	} else if (perfmt->flags & ULS_LF_PERCENT_G) {
		uls_lf_digits_to_percent_g(numstr, minus, n_expo, perfmt->precision, numbuf2);
	}

	wrdptr = (uls_lf_char_t *) csz_text(numbuf2) + numbuf_len2;
	len = csz_length(numbuf2) - numbuf_len2;

	len = uls_lf_fill_chars(ctx->x_dat, puts_proc, perfmt, wrdptr, len);

	csz_truncate(numbuf1, numbuf_len1);
	csz_truncate(numbuf2, numbuf_len2);

	return len;
}

static int
fmtproc_f(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	double num_f;

	num_f = (double) va_arg(ctx->args, double);
	return __fmtproc_f_e_g(ctx, puts_proc, num_f);
}

static int
fmtproc_e(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	double num_f;

	num_f = (double) va_arg(ctx->args, double);
	ctx->perfmt.flags |= ULS_LF_PERCENT_E;
	return __fmtproc_f_e_g(ctx, puts_proc, num_f);
}

static int
fmtproc_g(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	double num_f;

	num_f = (double) va_arg(ctx->args, double);
	ctx->perfmt.flags |= ULS_LF_PERCENT_G;
	return __fmtproc_f_e_g(ctx, puts_proc, num_f);
}

static int
__fmtproc_lf_le_lg(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc, long double num_lf)
{
	uls_lf_convflag_t* perfmt = &ctx->perfmt;
	uls_lf_char_t  *wrdptr;
	int  minus, n_expo, len;
	int  numbuf_len1, numbuf_len2;
	char   *numstr;

	csz_str_t  *numbuf1 = ctx->numbuf1;
	csz_str_t  *numbuf2 = ctx->numbuf2;

	numbuf_len1 = csz_length(numbuf1);
	numbuf_len2 = csz_length(numbuf2);

	if (num_lf < 0.) {
		minus = 1;
		num_lf = -num_lf;
	} else {
		minus = 0;
	}

	n_expo = uls_lf_longdouble2digits(num_lf, perfmt->precision, numbuf1);
	numstr = (char *) csz_text(numbuf1) + numbuf_len1;

	if ((perfmt->flags & (ULS_LF_PERCENT_E | ULS_LF_PERCENT_G))==0) {
		uls_lf_digits_to_percent_f(numstr, minus, n_expo, perfmt->precision, numbuf2);
	} else if (perfmt->flags & ULS_LF_PERCENT_E) {
		uls_lf_digits_to_percent_e(numstr, minus, n_expo, perfmt->precision, numbuf2);
	} else if (perfmt->flags & ULS_LF_PERCENT_G) {
		uls_lf_digits_to_percent_g(numstr, minus, n_expo, perfmt->precision, numbuf2);
	}

	wrdptr = (uls_lf_char_t *) csz_text(numbuf2) + numbuf_len2;
	len = csz_length(numbuf2) - numbuf_len2;

	len = uls_lf_fill_chars(ctx->x_dat, puts_proc, perfmt, wrdptr, len);

	csz_truncate(numbuf1, numbuf_len1);
	csz_truncate(numbuf2, numbuf_len2);

	return len;
}

static int
fmtproc_lf(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	long double num_lf;

	num_lf = (long double) va_arg(ctx->args, long double);
	return __fmtproc_lf_le_lg(ctx, puts_proc, num_lf);
}

static int
fmtproc_le(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	long double num_lf;

	num_lf = (long double) va_arg(ctx->args, long double);
	ctx->perfmt.flags |= ULS_LF_PERCENT_E;

	return __fmtproc_lf_le_lg(ctx, puts_proc, num_lf);
}

static int
fmtproc_lg(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	long double num_lf;

	num_lf = (long double) va_arg(ctx->args, long double);
	ctx->perfmt.flags |= ULS_LF_PERCENT_G;

	return __fmtproc_lf_le_lg(ctx, puts_proc, num_lf);
}

static int
fmtproc_Lf(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	err_panic("%%Lf: reserved for future!\n\tUse %%lf for 'long double' and %%f for 'double'");
	return -1;
}

static int
fmtproc_null(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	return 0;
}

static uls_lf_name2proc_t*
__realloc_convspec_table(uls_lf_convspec_table_t *tbl, int n)
{
	n += tbl->n_used;

	if (n > tbl->n_allocd) {
		n = uls_roundup(n, 16);
		tbl->proc_tab = uls_mrealloc(tbl->proc_tab, n * sizeof(uls_lf_name2proc_t));
		tbl->n_allocd = n;
	}

	return tbl->proc_tab;
}

static uls_lf_name2proc_t*
uls_lf_bi_search(const uls_lf_char_t* kwrd, int len, uls_lf_name2proc_t *ary, int n_ary)
{
	uls_lf_name2proc_t *e;
	int   low, high, mid, cond;

	low = 0;
	high = n_ary - 1;

	while (low <= high) {
		mid = (low + high) / 2;
		e = ary + mid;

		if ((cond=uls_lf_strncmp(kwrd, e->name, len))<0) {
			high = mid - 1;
		} else if (cond > 0) {
			low = mid + 1;
		} else {
			return e;
		}
	}

	return NULL;
}

static uls_lf_convspec_t
uls_lf_find_convspec(const uls_lf_char_t** p_fmtptr, uls_lf_map_t* map)
{
	const uls_lf_char_t *fmtptr = *p_fmtptr;
	uls_lf_convspec_table_t  *tbl;
	uls_lf_name2proc_t *e, *proc_map;
	int  ch, i, len;

	proc_map = map->linear.proc_tab;
	for (i=0; i<map->linear.n_used; i++) {
		if ((e = proc_map + i)->proc==NULL) continue;
		if (uls_lf_strncmp(e->name, fmtptr, e->l_name)==0) {
			*p_fmtptr = fmtptr + e->l_name;
			return e->proc;
		}
	}

	for (len=0; len < ULS_LF_PERCENT_NAMESIZ; len++) {
		if ((ch=fmtptr[len]) == '\0' || !isgraph(ch)) {
			break;
		}
	}

	if (len == 0) {
		return NULL;
	}

	for (i=len; i>0; i--) {
		tbl = map->sorted + get_perproc_table_ind(i);

		e = uls_lf_bi_search(fmtptr, i, tbl->proc_tab, tbl->n_used);
		if (e != NULL) { // found
			*p_fmtptr = fmtptr + i;
			return e->proc;
		}
	}

	return NULL;
}

int
__add_convspec_sorted(uls_lf_convspec_table_t *tbl,
	const uls_lf_char_t* percent_name, uls_lf_convspec_t proc)
{
	uls_lf_name2proc_t *e, *proc_map = tbl->proc_tab;
	uls_lf_convspec_table_t  *tbl2;
	int j, rc, len;

	len = uls_lf_strlen(percent_name);

	for (j=0; ; j++) {
		if (j >= tbl->n_used) {
			proc_map = __realloc_convspec_table(tbl, 1);
			++tbl->n_used;
			break;
		}

		if ((rc=uls_lf_strcmp(proc_map[j].name, percent_name))==0) {
			if (proc != NULL) {
				__uls_lf_sysputs("overriding pecent convspec, ...");
			} else {
				tbl2 = dfl_convspec_map.sorted + get_perproc_table_ind(len);

				e = uls_lf_bi_search(percent_name, len, tbl2->proc_tab, tbl2->n_used);
				if (e==NULL) {
					proc = fmtproc_null;
				} else {
					proc = e->proc; // default action
				}
			}
			break;

		} else if (rc > 0) {
			proc_map = __realloc_convspec_table(tbl, 1);
			memmove(proc_map + j+1, proc_map + j, (tbl->n_used - j) *sizeof(uls_lf_name2proc_t) );
			++tbl->n_used;
			break;
		}
	}

	uls_lf_strcpy(proc_map[j].name, percent_name);
	proc_map[j].l_name = len;
	proc_map[j].proc = proc;

	return j;
}

void
__add_convspec_linear(uls_lf_convspec_table_t *tbl,
	uls_lf_char_t* percent_name, uls_lf_convspec_t proc, int tbl_ind)
{
	uls_lf_name2proc_t *e, *proc_map;
	int j;

	if (tbl_ind >= tbl->n_used) {
		proc_map = __realloc_convspec_table(tbl, tbl_ind+1-tbl->n_used);
		for (j=tbl->n_used; j<=tbl_ind; j++) {
			proc_map[j].name[0] = '\0';
			proc_map[j].proc = NULL;
		}
		tbl->n_used = tbl_ind + 1;
	} else {
		proc_map = tbl->proc_tab;
	}

	// No check for duplicate elements.
	e = proc_map + tbl_ind;
	if (e->proc != NULL)
		__uls_lf_sysputs("overriding pecent convspec , ...");

	uls_lf_strcpy(e->name, percent_name);
	e->l_name = uls_lf_strlen(percent_name);
	e->proc = proc;
}

static int
__replace_convspec_linear(uls_lf_convspec_table_t *tbl,
	const uls_lf_char_t* percent_name, uls_lf_convspec_t proc)
{
	uls_lf_name2proc_t *e, *proc_map;
	int i;

	proc_map = tbl->proc_tab;
	for (i=0; i<tbl->n_used; i++) {
		if ((e = proc_map + i)->proc==NULL) continue;

		if (uls_lf_strcmp(e->name, percent_name)==0) {
			__uls_lf_sysputs("overriding pecent func, ...");
			e->proc = proc;
			return 1;
		}
	}
	
	return 0;
}

uls_lf_puts_t
__uls_lf_change_puts(uls_lf_t *uls_lf, uls_lf_puts_t puts_proc)
{
	uls_lf_puts_t old_proc;

	if (uls_lf == NULL) uls_lf = dfl_uls_lf;

	old_proc = uls_lf->uls_lf_puts;
	if (puts_proc == NULL) {
		uls_lf->uls_lf_puts = uls_lf_puts_null;
	} else {
		uls_lf->uls_lf_puts = puts_proc;
	}
	
	return old_proc;
}

// <brief>
// You can use this method to change the default output interface for logging.
// </brief>
// <parm name="uls_lf">uls-lf context</parm>
// <parm name="puts_proc">A newly output interface</parm>
// <return>none</return>
void
uls_lf_change_puts(uls_lf_t *uls_lf, uls_lf_puts_t puts_proc)
{
	uls_lock_mutex(&uls_lf->mtx);
	__uls_lf_change_puts(uls_lf, puts_proc);
	uls_unlock_mutex(&uls_lf->mtx);
}

// <brief>
// Changes the associated procedure with 'percent_name', a converion specification.
// The procedure will output string with puts_proc which can be set by 'changePuts'
// </brief>
// <parm name="uls_lf">uls-lf context</parm>
// <parm name="percent_name">A converion specification without the percent character itself.</parm>
// <parm name="proc">The user defined procedure for processing '%percent_name'</parm>
// <return>0 if it suceeds, otherwise -1</return>
int
uls_lf_register_convspec(uls_lf_t *uls_lf,
	const uls_lf_char_t* percent_name, uls_lf_convspec_t proc)
{
	uls_lf_map_t *map_dst;
	uls_lf_convspec_table_t  *tbl;
	int len, ch;

	if (uls_lf == NULL)
		uls_lf = dfl_uls_lf;

	for (len=0; (ch=percent_name[len])!='\0'; len++) {
		if (!isgraph(ch)) {
			__uls_lf_sysputs("uls_lf_register_convspec: The percent-name contains illegal char");
			return -1;
		}
	}

	if (len > ULS_LF_PERCENT_NAMESIZ || len <= 0) {
		__uls_lf_sysputs("Too long percent func, ...");
		return -1;
	}

	uls_lock_mutex(&uls_lf->mtx);

	map_dst = &uls_lf->convspec_map;

	if (__replace_convspec_linear(&map_dst->linear,
		percent_name, proc)==0) {
		tbl = map_dst->sorted + get_perproc_table_ind(len);
		__add_convspec_sorted(tbl, percent_name, proc);
	}

	uls_unlock_mutex(&uls_lf->mtx);

	return 0;
}

static void
copy_convspec_map(uls_lf_map_t* map_src, uls_lf_map_t* map_dst)
{
	uls_lf_convspec_table_t  *tbl_dst, *tbl_src;
	int i;

	tbl_dst = &map_dst->linear;
	tbl_src = &map_src->linear;

	__realloc_convspec_table(tbl_dst, tbl_src->n_used);

	memcpy(tbl_dst->proc_tab, tbl_src->proc_tab,
		tbl_src->n_used * sizeof(uls_lf_name2proc_t));
	tbl_dst->n_used = tbl_src->n_used;

	for (i=0; i < ULS_LF_PERCENT_NAMESIZ; i++) {
		tbl_dst = map_dst->sorted + i;
		tbl_src = map_src->sorted + i;

		__realloc_convspec_table(tbl_dst, tbl_src->n_used);
		memcpy(tbl_dst->proc_tab, tbl_src->proc_tab,
			tbl_src->n_used * sizeof(uls_lf_name2proc_t));
		tbl_dst->n_used = tbl_src->n_used;
	}
}

static void
uls_lf_init_convspec_map(uls_lf_map_t* map_dst)
{
	uls_lf_convspec_table_t  *tbl_dst;
	int i;

	tbl_dst = &map_dst->linear;
	tbl_dst->proc_tab = NULL;
	tbl_dst->n_used = tbl_dst->n_allocd = 0;

	for (i=0; i < ULS_LF_PERCENT_NAMESIZ; i++) {
		tbl_dst = map_dst->sorted + i;
		tbl_dst->proc_tab = NULL;
		tbl_dst->n_used = tbl_dst->n_allocd = 0;
	}
}

void
uls_lf_init(uls_lf_t *uls_lf, uls_lf_puts_t puts_proc)
{
	if (puts_proc == NULL) {
		uls_lf->uls_lf_puts = uls_lf_puts_null;
	} else {
		uls_lf->uls_lf_puts = puts_proc;
	}

	uls_init_mutex(&uls_lf->mtx);

	uls_lf_init_convspec_map(&uls_lf->convspec_map);
	copy_convspec_map(&dfl_convspec_map, &uls_lf->convspec_map);

	csz_init(&uls_lf->zbuf1, ULS_MAXSZ_NUMSTR);
	csz_init(&uls_lf->zbuf2, ULS_MAXSZ_NUMSTR);
}

static void
free_convspec_map(uls_lf_map_t* map_dst)
{
	uls_lf_convspec_table_t  *tbl;
	int i;

	for (i=0; i < ULS_LF_PERCENT_NAMESIZ; i++) {
		tbl = map_dst->sorted + i;
		tbl->proc_tab = uls_mfree(tbl->proc_tab);
		tbl->n_used = tbl->n_allocd = 0;
	}

	tbl = &map_dst->linear;
	tbl->proc_tab = uls_mfree(tbl->proc_tab);
	tbl->n_used = tbl->n_allocd = 0;
}

void
uls_lf_deinit(uls_lf_t *uls_lf)
{
	free_convspec_map(&uls_lf->convspec_map);
	csz_deinit(&uls_lf->zbuf1);
	csz_deinit(&uls_lf->zbuf2);
	uls_deinit_mutex(&uls_lf->mtx);
}

uls_lf_t*
uls_lf_create(uls_lf_puts_t puts_proc)
{
	uls_lf_t *uls_lf;

	uls_lf = (uls_lf_t *) uls_malloc(sizeof(uls_lf_t));
	uls_lf_init(uls_lf, puts_proc);

	return uls_lf;
}

void
uls_lf_destroy(uls_lf_t *uls_lf)
{
	uls_lf_deinit(uls_lf);
	uls_mfree(uls_lf);
}

static int
__uls_lf_skip_atou(const uls_lf_char_t ** p_ptr)
{
	const uls_lf_char_t   *ptr = *p_ptr;
	uls_lf_char_t   ch;
	int     n;

	for (n=0; uls_lf_isdigit(ch=*ptr); ptr++) // ch >= '0 && ch <= '9'
		n = n*10 + (ch - '0');
	*p_ptr = ptr;
	return n;
}

const uls_lf_char_t*
pars_perfmt(uls_lf_convflag_t *p, const uls_lf_char_t* fmt)
{
	uls_lf_convflag_t nilspec;
	uls_lf_char_t  ch;

	if (p==NULL)
		p = &nilspec;

	p->flags = 0;
	p->width = -1;
	p->precision = ULS_LF_DFL_FLOAT_PRECISION;

	/* process flags */
	while (1) {
		ch = *fmt;
		if (ch == '-') p->flags |= ULS_LF_LEFT_JUSTIFIED;
		else if (ch == '+') p->flags |= ULS_LF_PLUS_PREFIX;
		else if (ch == '#') p->flags |= ULS_LF_SPECIAL_CHAR;
		else if (ch == '0') p->flags |= ULS_LF_ZEROPAD;
		else break;
		++fmt;
	}

	/* get field width */
	if (ch == '*') {
		p->flags |= ULS_LF_DYNAMIC_WIDTH;
		++fmt;
	} else if (isdigit(ch)) {
		// ch != '0'
		p->width = __uls_lf_skip_atou(&fmt);
		/* p->width >= 0 */

	}

	if (*fmt=='.') {
		if ((ch=*++fmt) == '*') {
			p->flags |= ULS_LF_DYNAMIC_PRECISION;
			++fmt;
		} else if (isdigit(ch)) {
			p->precision = __uls_lf_skip_atou(&fmt);
		}
	}

	if (p->flags & ULS_LF_LEFT_JUSTIFIED) p->flags &= ~ULS_LF_ZEROPAD;

	return fmt;
}

int
__uls_lf_vxprintf(void *x_dat, uls_lf_t* uls_lf, void* g_dat, const uls_lf_char_t* fmtstr, va_list args)
{
	uls_lf_puts_t     puts_proc = uls_lf->uls_lf_puts;
	uls_lf_convspec_t perc_proc;

	int               buflen = 0, len, zbuf_len1, zbuf_len2;
	const uls_lf_char_t     *fmtptr, *wrdptr;
	uls_lf_char_t     ch;
	uls_lf_context_t  ctx;

	ctx.x_dat = x_dat;
	ctx.g_dat = g_dat;
	ctx.args = args;

	zbuf_len1 = csz_length(&uls_lf->zbuf1);
	zbuf_len2 = csz_length(&uls_lf->zbuf2);

	ctx.numbuf1 = &uls_lf->zbuf1;
	ctx.numbuf2 = &uls_lf->zbuf2;

	wrdptr = fmtptr = fmtstr;
	for ( ; ; ) {
		for ( ; (ch=*fmtptr)!='%'; fmtptr++) {
			if (ch=='\0') break; 
		}

		if ((len = (int) (fmtptr - wrdptr)) > 0) {
			puts_proc(x_dat, wrdptr, len*sizeof(uls_lf_char_t));
			buflen += len;
		}

		if (ch=='\0') {
			puts_proc(x_dat, NULL, 0);
			break; 
		}

		wrdptr = fmtptr++;
		if (*fmtptr == '%') {
			wrdptr = fmtptr++;
			continue;
		}

		fmtptr = pars_perfmt(&ctx.perfmt, fmtptr);

		if ((perc_proc=uls_lf_find_convspec(&fmtptr, &uls_lf->convspec_map)) == NULL) {
			fmtptr = wrdptr + 1;
			if ((perc_proc=uls_lf_find_convspec(&fmtptr, &uls_lf->convspec_map)) == NULL) {
				continue;
			}
		}

		wrdptr = fmtptr;

		if (ctx.perfmt.flags & ULS_LF_DYNAMIC_WIDTH) {
			ctx.perfmt.width = (int) va_arg(ctx.args, int);
			if (ctx.perfmt.width < 0) {
				ctx.perfmt.flags |= ULS_LF_LEFT_JUSTIFIED;
				ctx.perfmt.width = -ctx.perfmt.width;
			}
		}

		if (ctx.perfmt.flags & ULS_LF_DYNAMIC_PRECISION) {
			ctx.perfmt.precision = (int) va_arg(ctx.args, int);
			if (ctx.perfmt.precision < 0) {
				ctx.perfmt.precision = ULS_LF_DFL_FLOAT_PRECISION;
			}
		}

		if ((len=perc_proc(&ctx, puts_proc)) < 0) {
			break;
		}
		buflen += len;
	}

	csz_truncate(&uls_lf->zbuf1, zbuf_len1);
	csz_truncate(&uls_lf->zbuf2, zbuf_len2);

	return buflen; 
}

int
uls_lf_vxprintf(void *x_dat, uls_lf_t* uls_lf, void* g_dat, const uls_lf_char_t* fmtstr, va_list args)
{
	int len;

	uls_lock_mutex(&uls_lf->mtx);
	len = __uls_lf_vxprintf(x_dat, uls_lf, g_dat, fmtstr, args);
	uls_unlock_mutex(&uls_lf->mtx);

	return len;
}

int
uls_lf_xprintf(void* x_dat, uls_lf_t* uls_lf, void* g_dat, const uls_lf_char_t* fmt, ...)
{
	va_list args;
	int len;

	va_start(args, fmt);
	len = uls_lf_vxprintf(x_dat, uls_lf, g_dat, fmt, args);
	va_end(args);

	return len;
}

void
uls_lf_csz_init(uls_lf_csz_t* bb, uls_lf_t* uls_lf)
{
	if (uls_lf == NULL) uls_lf = dfl_uls_lf;
	csz_init(&bb->csz, 128);
	bb->uls_lf = uls_lf;
}

void
uls_lf_csz_reset(uls_lf_csz_t* bb, uls_lf_t* uls_lf)
{
	if (uls_lf == NULL) uls_lf = dfl_uls_lf;
	csz_reset(&bb->csz);
	bb->uls_lf = uls_lf;
}

void
uls_lf_csz_deinit(uls_lf_csz_t* bb)
{
	csz_deinit(&bb->csz);
	bb->uls_lf = NULL;
}

int uls_lf_vzprintf(uls_lf_csz_t* bb, const uls_lf_char_t *fmt, va_list args)
{
	return uls_lf_vxprintf_generic(NULL, &bb->csz, uls_lf_puts_csz, NULL, fmt, args);
}

int uls_lf_zprintf_append(uls_lf_csz_t* bb, const uls_lf_char_t *fmt, ...)
{
	va_list args;
	int len;

	va_start(args, fmt);
	len = uls_lf_vzprintf(bb, fmt, args);
	va_end(args);

	return len;
}

int uls_lf_zprintf(uls_lf_csz_t* bb, const uls_lf_char_t *fmt, ...)
{
	va_list args;
	int len;

	csz_reset(&bb->csz);

	va_start(args, fmt);
	len = uls_lf_vzprintf(bb, fmt, args);
	va_end(args);

	return len;
}

int
uls_lf_vxprintf_generic(uls_lf_t* uls_lf, void* x_dat, uls_lf_puts_t puts_proc,
	void* g_dat, const uls_lf_char_t* fmtstr, va_list args)
{
	uls_lf_puts_t old_proc;
	int len;

	if (uls_lf == NULL) uls_lf = dfl_uls_lf;
	if (puts_proc == NULL) puts_proc = uls_lf_puts_null;

	uls_lock_mutex(&uls_lf->mtx);
	old_proc = __uls_lf_change_puts(uls_lf, puts_proc);
	len = __uls_lf_vxprintf(x_dat, uls_lf, g_dat, fmtstr, args);
	__uls_lf_change_puts(uls_lf, old_proc);
	uls_unlock_mutex(&uls_lf->mtx);

	return len;
}

void
initialize_uls_lf(void)
{
	uls_lf_convspec_table_t  *tbl;

	uls_lf_init_convspec_map(&dfl_convspec_map);
	uls_init_mutex(&uls_lf_genport_mtx);

	tbl = &dfl_convspec_map.linear;
	__add_convspec_linear(tbl, "s", fmtproc_s, ULS_LF_PERCPROC_s);
	__add_convspec_linear(tbl, "c", fmtproc_c, ULS_LF_PERCPROC_c);

	__add_convspec_linear(tbl, "d", fmtproc_d, ULS_LF_PERCPROC_d);
	__add_convspec_linear(tbl, "u", fmtproc_u, ULS_LF_PERCPROC_u);
	__add_convspec_linear(tbl, "f", fmtproc_f, ULS_LF_PERCPROC_f);

	__add_convspec_linear(tbl, "ls", fmtproc_ls, ULS_LF_PERCPROC_ws);
	__add_convspec_linear(tbl, "lc", fmtproc_lc, ULS_LF_PERCPROC_wc);
	__add_convspec_linear(tbl, "lf", fmtproc_lf, ULS_LF_PERCPROC_lf);

	__add_convspec_linear(tbl, "Ld", fmtproc_Ld, ULS_LF_PERCPROC_Ld);
	__add_convspec_linear(tbl, "Lu", fmtproc_Lu, ULS_LF_PERCPROC_Lu);
	__add_convspec_linear(tbl, "Lf", fmtproc_Lf, ULS_LF_PERCPROC_Lf);

	__add_convspec_linear(tbl, "ld", fmtproc_ld, ULS_LF_PERCPROC_ld);
	__add_convspec_linear(tbl, "lu", fmtproc_lu, ULS_LF_PERCPROC_lu);

	__add_convspec_linear(tbl, "p", fmtproc_p, ULS_LF_PERCPROC_p);

	tbl = dfl_convspec_map.sorted + get_perproc_table_ind(1);
	__add_convspec_sorted(tbl, "x", fmtproc_x);
	__add_convspec_sorted(tbl, "X", fmtproc_X);
	__add_convspec_sorted(tbl, "o", fmtproc_o);
	__add_convspec_sorted(tbl, "e", fmtproc_e);
	__add_convspec_sorted(tbl, "g", fmtproc_g);

	tbl = dfl_convspec_map.sorted + get_perproc_table_ind(2);
	__add_convspec_sorted(tbl, "lx", fmtproc_lx);
	__add_convspec_sorted(tbl, "Lx", fmtproc_Lx);
	__add_convspec_sorted(tbl, "lX", fmtproc_lX);
	__add_convspec_sorted(tbl, "LX", fmtproc_LX);
	__add_convspec_sorted(tbl, "le", fmtproc_le);
	__add_convspec_sorted(tbl, "lg", fmtproc_lg);

	tbl = dfl_convspec_map.sorted + get_perproc_table_ind(3);
	__add_convspec_sorted(tbl, "lld", fmtproc_Ld);
	__add_convspec_sorted(tbl, "llu", fmtproc_Lu);

	dfl_uls_lf = uls_lf_create(uls_lf_puts_str);
}

void
finalize_uls_lf(void)
{
	uls_lf_destroy(dfl_uls_lf);
	free_convspec_map(&dfl_convspec_map);
	uls_deinit_mutex(&uls_lf_genport_mtx);
}

static uls_lf_char_t _uls_lf_digits[] = "0123456789ABCDEF";

int
uls_lf_number_u(uls_lf_char_t *numstr, unsigned int num, int base_shift)
{
	unsigned int  base_mask;
	register int   i;

	i = ULS_MAXSZ_NUMSTR; numstr[ULS_MAXSZ_NUMSTR] = '\0';

	if (num == 0) {
		numstr[--i] = '0';
		return i;
	}

	if (base_shift==ULS_LF_SHIFT_BASE_DEC) {
		do {
			numstr[--i] = _uls_lf_digits[(int) (num%10)];
			num /= 10;
		} while (num!=0);

	} else if (base_shift==ULS_LF_SHIFT_BASE_OCT) {
		do {
			numstr[--i] = '0' + (num % 8);
			num /= 8;
		} while (num!=0);

	} else {
		base_mask = (1 << base_shift) - 1;

		do {
			numstr[--i] = _uls_lf_digits[ (int) (num & base_mask) ];
			num >>= base_shift;
		} while (num!=0);
	}

	return i;
}

int
uls_lf_number_lu(uls_lf_char_t *numstr, unsigned long num, int base_shift)
{
	unsigned long  base_mask;
	register int   i;

	i = ULS_MAXSZ_NUMSTR; numstr[ULS_MAXSZ_NUMSTR] = '\0';

	if (num == 0) {
		numstr[--i] = '0';
		return i;
	}

	if (base_shift==ULS_LF_SHIFT_BASE_DEC) {
		do {
			numstr[--i] = _uls_lf_digits[(int) (num%10)];
			num /= 10;
		} while (num!=0);

	} else {
		base_mask = (1 << base_shift) - 1;

		do {
			numstr[--i] = _uls_lf_digits[ (int) (num & base_mask) ];
			num >>= base_shift;
		} while (num!=0);
	}

	return i;
}

int 
uls_lf_number_Lu(uls_lf_char_t *numstr, unsigned long long num, int base_shift)
{
	unsigned long long base_mask;
	register int   i;

	i = ULS_MAXSZ_NUMSTR; numstr[ULS_MAXSZ_NUMSTR] = '\0';

	if (num == 0) {
		numstr[--i] = '0';
		return i;
	}

	if (base_shift==ULS_LF_SHIFT_BASE_DEC) {
		do {
			numstr[--i] = _uls_lf_digits[(int) (num % 10)];
			num /= 10;
		} while (num!=0);

	} else {
		base_mask = (1 << base_shift) - 1;
		do {
			numstr[--i] = _uls_lf_digits[ (int) (num & base_mask) ];
			num >>= base_shift;
		} while (num!=0);
	}

	return i;
}

int
uls_lf_puts_prefix(uls_lf_char_t* str, int flags)
{
	int   k = 0;

	if (flags & ULS_LF_MINUS_PREFIX) {
		str[k++] = '-'; 
	} else if (flags & ULS_LF_PLUS_PREFIX) {
		str[k++] = '+'; 
	}

	if (flags & ULS_LF_HEX_PREFIX) {
		str[k++] = '0'; str[k++] = 'x';
	} else  if (flags & ULS_LF_BIN_PREFIX) {
		str[k++] = '0'; str[k++] = 'b';
	}

	str[k] = '\0';

	return k;
}

void uls_lf_fill_ch(void *x_dat, uls_lf_puts_t puts_proc, uls_lf_char_t ch_fill, int n)
{
	uls_lf_char_t fillstr[ULS_LF_N_FILLCHS];
	int i, m;

	if (n==0) return;

	for (i=0; i<ULS_LF_N_FILLCHS; i++) 
		fillstr[i] = ch_fill;

	for (i=0; i < n / ULS_LF_N_FILLCHS; i++) {
		puts_proc(x_dat, fillstr, ULS_LF_N_FILLCHS*sizeof(uls_lf_char_t));
	}

	if ((m=n % ULS_LF_N_FILLCHS) != 0) 
		puts_proc(x_dat, fillstr, m*sizeof(uls_lf_char_t));
}

int uls_lf_fill_chars(void *x_dat, uls_lf_puts_t puts_proc,
	uls_lf_convflag_t* p, uls_lf_char_t* numstr, int l_numstr)
{
	int     width, len1, len2, len3;
	uls_lf_char_t    prefix[16], *ptr1;
	uls_lf_char_t *ptr2;

	len1 = uls_lf_puts_prefix(prefix, p->flags);
	len2 = l_numstr;
	len3 = len1 + len2; /* prefix + number_str */

	if ((width=p->width)<0) width = len3; 
	else if (len3 > width) width = len3; 

	ptr1 = prefix;
	ptr2 = numstr;

	if (p->flags & ULS_LF_LEFT_JUSTIFIED) {
		puts_proc(x_dat, ptr1, len1*sizeof(uls_lf_char_t));
		puts_proc(x_dat, ptr2, len2*sizeof(uls_lf_char_t));
		uls_lf_fill_ch(x_dat, puts_proc, ' ', width - len3);
	} else {
		if (p->flags & ULS_LF_ZEROPAD) {
			puts_proc(x_dat, ptr1, len1*sizeof(uls_lf_char_t));
			uls_lf_fill_ch(x_dat, puts_proc, '0', width - len3);
			puts_proc(x_dat, ptr2, len2*sizeof(uls_lf_char_t));
		} else {
			uls_lf_fill_ch(x_dat, puts_proc, ' ', width - len3);
			puts_proc(x_dat, ptr1, len1*sizeof(uls_lf_char_t));
			puts_proc(x_dat, ptr2, len2*sizeof(uls_lf_char_t));
		}
	}

	return width;
}

int uls_lf_fill_mbstr(void *x_dat, uls_lf_puts_t puts_proc,
	uls_lf_convflag_t* p, uls_lf_char_t* numstr, int l_numstr, int lw_numstr)
{
	int     width, len2;

	if ((width=p->width) < 0) width = lw_numstr; 
	else if (lw_numstr > width) width = lw_numstr; 

	len2 = width - lw_numstr;

	if (p->flags & ULS_LF_LEFT_JUSTIFIED) {
		puts_proc(x_dat, numstr, l_numstr);
		uls_lf_fill_ch(x_dat, puts_proc, ' ', len2);
	} else {
		if (p->flags & ULS_LF_ZEROPAD) {
			uls_lf_fill_ch(x_dat, puts_proc, '0', len2);
			puts_proc(x_dat, numstr, l_numstr);
		} else {
			uls_lf_fill_ch(x_dat, puts_proc, ' ', len2);
			puts_proc(x_dat, numstr, l_numstr);
		}
	}

	return len2 + l_numstr;
}
